/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package org.springframework.beans.factory.xml;

import java.io.InputStream;

import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.parsing.EmptyReaderEventListener;
import org.springframework.beans.factory.parsing.FailFastProblemReporter;
import org.springframework.beans.factory.parsing.NullSourceExtractor;
import org.springframework.beans.factory.parsing.ProblemReporter;
import org.springframework.beans.factory.parsing.ReaderEventListener;
import org.springframework.beans.factory.parsing.SourceExtractor;
import org.springframework.beans.factory.support.AbstractBeanDefinitionReader;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.EncodedResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.util.xml.SimpleSaxErrorHandler;
import org.springframework.util.xml.XmlValidationModeDetector;
import org.w3c.dom.Document;
import org.xml.sax.EntityResolver;
import org.xml.sax.ErrorHandler;
import org.xml.sax.InputSource;

/**
 *
 * @author lxz
 */
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {

    public static final int VALIDATION_AUTO = XmlValidationModeDetector.VALIDATION_AUTO;
    
    public static final int VALIDATION_XSD = XmlValidationModeDetector.VALIDATION_AUTO;
    /** 验证xml文件头引用的是dtd 或 schema */
    private final XmlValidationModeDetector validationModeDetector = new XmlValidationModeDetector();
    
    private DocumentLoader documentLoader = new DefaultDocumentLoader();
    
    private ErrorHandler errorHandler = new SimpleSaxErrorHandler(logger);
    
    private EntityResolver entityResolver;
    
    private ResourceLoader resourceLoader;
    
    private boolean namespaceAware = false;
    
    private BeanFactory beanFactory;
    
    private ClassLoader beanClassLoader;
    
    private Class documentReaderClass = DefaultBeanDefinitionDocumentReader.class;
    
    private ProblemReporter problemReporter = new FailFastProblemReporter();
    
    private ReaderEventListener eventListener = new EmptyReaderEventListener();
    
    private SourceExtractor sourceExtractor = new NullSourceExtractor();
    
    private NamespaceHandlerResolver namespaceHandlerResolver;
    
    private int validationMode = VALIDATION_AUTO;

    public XmlBeanDefinitionReader(BeanDefinitionRegistry registry) {
        super(registry);
    }
    
    public int loadBeanDefinitions(Resource resource) throws Exception {
        return loadBeanDefinitions(new EncodedResource(resource));
    }

    public int loadBeanDefinitions(EncodedResource encodedResource) throws Exception {
        InputStream inputStream = encodedResource.getResource().getInputStream();
        try {
            InputSource inputSource = new InputSource(inputStream);
            return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
        } finally {
            inputStream.close();
        }
    }

    protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws Exception {
        int validationMode = getValidationModeForResource(resource);
        Document doc = this.documentLoader.loadDocument(inputSource, this.getEntityResolver(), errorHandler, validationMode, namespaceAware);
        return registerBeanDefinitions(doc, resource);
    }

    private int registerBeanDefinitions(Document doc, Resource resource) {
        BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
        int beforeCount = this.getRegistry().getBeanDefinitionCount();
        documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
        return getRegistry().getBeanDefinitionCount() - beforeCount;
    }

    protected XmlReaderContext createReaderContext(Resource resource) {
        if (namespaceHandlerResolver == null) {
            namespaceHandlerResolver = createDefaultNamespaceHandlerResolver();
        }
        return new XmlReaderContext(resource,this.problemReporter, this.eventListener,
                this.sourceExtractor, this, this.namespaceHandlerResolver);
    }
    private NamespaceHandlerResolver createDefaultNamespaceHandlerResolver() {
        return new DefaultNamespaceHandlerResolver(this.getResourceLoader().getClassLoader());
    }

    @SuppressWarnings("unchecked")
    private BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() {
        return BeanDefinitionDocumentReader.class.cast(BeanUtils.instantiateClass(this.documentReaderClass));
    }

    protected int getValidationModeForResource(Resource resource) {
        int validationModeToUse = getValidationMode();
        if (validationModeToUse != VALIDATION_AUTO) {
            return validationModeToUse;
        }
        int detectedMode = detectValidationMode(resource);
        if (detectedMode != VALIDATION_AUTO) {
            return detectedMode;
        }
        return VALIDATION_XSD;
    }

    /**
     * 判断xml文件是用dtd文档验证，还是 schema文档验证
     * @param resource
     * @return
     */
    protected int detectValidationMode(Resource resource) {
        try {
            InputStream inputStream = resource.getInputStream();
            return this.validationModeDetector.detectValidationMode(inputStream);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }
    public int getValidationMode() {
        return this.validationMode;
    }

    public boolean isNamespaceAware() {
        return namespaceAware;
    }

    public void setNamespaceAware(boolean namespaceAware) {
        this.namespaceAware = namespaceAware;
    }

    public EntityResolver getEntityResolver() {
        if (this.entityResolver == null) {
            ResourceLoader resourceLoader = getResourceLoader();
            if (resourceLoader != null) {
                entityResolver = new ResourceEntityResolver(resourceLoader);
            } else {
                entityResolver = new DelegatingEntityResolver(getBeanClassLoader());
            }
        }
        return entityResolver;
    }

    public void setEntityResolver(EntityResolver entityResolver) {
        this.entityResolver = entityResolver;
    }

    public ClassLoader getBeanClassLoader() {
        return beanClassLoader;
    }

    public void setBeanClassLoader(ClassLoader beanClassLoader) {
        this.beanClassLoader = beanClassLoader;
    }

}
